#include <Arduino.h>
#include <U8g2lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
/*=========================================================
                      自定义按键
  =========================================================*/
#define UP 17//定义方向键上为Arduino上的11号引脚
#define DOWN 2//定义方向键上为Arduino上的10号引脚
#define LEFT 15//定义方向键上为Arduino上的9号引脚
#define RIGHT 3//定义方向键上为Arduino上的8号引脚
#define A 4//定义方向键上为Arduino上的7号引脚
#define B 16//定义方向键上为Arduino上的6号引脚
/*=========================================================
                         位图
  =========================================================*/
//HWT 作者信息 请勿删除
const uint8_t HWT[] PROGMEM = {0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcc,0xc0,0x98,0x79,0x90,0xa2,0x0f,0x22,0x45,0x84,0x20,0x25,0x49,0x90,0x22,0x02,0x22,0x45,0x32,0x81,0x24,0x79,0xf0,0x22,0x62,0xe2,0x45,0x32,0x41,0x24,0x49,0x90,0x2a,0x62,0x22,0x55,0x84,0x20,0x24,0x49,0x90,0x14,0x02,0x22,0x29,0xcc,0xe0,0x99,0x7b,0x90,0x14,0x02,0x2e,0x29,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
//社会核心价值观
const uint8_t CSV[] PROGMEM = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0xf7,0xff,0xfe,0xf7,0xf9,0xbf,0xff,0x7b,0xff,0xbd,0xff,0x07,0xfe,0xff,0xff,0xff,0xf7,0x7f,0xfd,0xf7,0xfb,0x7f,0xff,0x7b,0xff,0x01,0x10,0xf4,0xfe,0xff,0xff,0x87,0xf7,0xbf,0xfb,0x37,0x80,0x7f,0xfe,0xbd,0xfe,0xbe,0xff,0xd5,0xfe,0xff,0xff,0xbf,0xf7,0xdf,0xf7,0xc1,0xfd,0x6f,0xff,0xdd,0xfd,0xbe,0xdf,0xd5,0xfe,0xff,0xff,0xdf,0x00,0xef,0xef,0xf7,0xfe,0xef,0xff,0xec,0x7b,0x06,0xbc,0xd6,0xfe,0xff,0xff,0xdf,0xf7,0x13,0x10,0x73,0xef,0xed,0x77,0xf5,0xa7,0xf6,0xbd,0xd6,0xfe,0xff,0x1f,0x8c,0xf7,0xff,0xbf,0x63,0xe0,0xed,0xef,0xd9,0xf5,0x06,0x7c,0xd7,0x1e,0xfc,0xff,0x57,0xf7,0xff,0xff,0xd5,0xf7,0xed,0xdf,0xdd,0xfd,0xf6,0x7d,0xd7,0xfe,0xff,0xff,0xdf,0xf7,0x07,0xc0,0xf5,0xdb,0xed,0xdf,0xdd,0xfd,0x06,0xbc,0xd6,0xfe,0xff,0xff,0xdf,0xf7,0x7f,0xff,0xf7,0xcc,0xee,0xdf,0xdd,0xfd,0xf6,0xbd,0x95,0xfe,0xff,0xff,0xdf,0xf7,0xbf,0xfb,0x37,0xf7,0xef,0xf7,0xdd,0xfd,0x06,0xbc,0xad,0xff,0xff,0xff,0xdf,0xf7,0xdf,0xf7,0xf7,0xfb,0xef,0xf7,0xdd,0xfd,0xf6,0xdd,0xb7,0xfb,0xff,0xff,0x5f,0x00,0x0e,0xe0,0xf7,0xe4,0xef,0xf7,0xed,0xfd,0xf6,0xfd,0xbb,0xfb,0xff,0xff,0xdf,0xff,0xdf,0xef,0x17,0xdf,0x1f,0xf8,0xf5,0xfd,0x00,0xf0,0x7c,0xfc,0xff,0xff,0xfb,0xff,0xff,0xff,0xff,0xbf,0xff,0xdf,0xff,0xff,0xff,0xf7,0xff,0xbd,0xff,0x0f,0x00,0x84,0x01,0x07,0xc0,0x7f,0xff,0xbf,0xff,0x3f,0xf0,0xe3,0xbf,0xbd,0xfd,0xef,0xff,0xbd,0x7d,0xf7,0xdf,0x7f,0xff,0xbf,0x7f,0xb0,0x37,0xfc,0x7f,0xa1,0xfe,0x2f,0x80,0xbe,0x7d,0xf7,0xdf,0x03,0xc0,0x00,0x40,0xb7,0xf7,0xfd,0x7f,0x3d,0xff,0xff,0xff,0xbf,0x01,0x07,0xc0,0x7f,0xff,0xf7,0x7b,0x37,0xf0,0x1d,0xf8,0xbd,0xfb,0x7f,0xc0,0x87,0xef,0xf7,0xfe,0x7f,0xff,0xf7,0x7b,0xb0,0x37,0xc0,0xfb,0xa5,0xfb,0x7f,0xdf,0xf7,0xef,0xf7,0xfe,0x7f,0xff,0xf7,0x7d,0xb7,0xf7,0xdd,0x1b,0x59,0xfc,0x7f,0xc0,0xf7,0x00,0x06,0x80,0x07,0xe0,0xef,0x7d,0xb7,0xf7,0xdd,0x7b,0xef,0xff,0xff,0xff,0x87,0xee,0xf6,0xfd,0x7f,0xff,0xef,0x7e,0x37,0xf0,0xd8,0x7b,0x03,0xfe,0x3f,0x80,0xbf,0xee,0xf6,0xfd,0x7f,0xff,0xdf,0x7e,0xb0,0x77,0xd5,0x7b,0xfb,0xfe,0xbf,0xbb,0xbf,0x00,0xf6,0xfb,0x7f,0xff,0x3f,0x7f,0xb7,0x77,0xd5,0x7b,0xfb,0xfe,0x3f,0x80,0xbf,0xef,0xb7,0x77,0x7f,0xff,0xdf,0xfc,0xdf,0xb7,0xdd,0x7b,0x01,0xfe,0xbf,0xbb,0xb7,0x6f,0xd7,0x6f,0x7f,0xff,0xef,0xfb,0xdf,0xf7,0x1d,0x78,0xfa,0xfe,0x3f,0x80,0xaf,0x00,0xe6,0x1f,0x01,0x80,0xf3,0xc7,0xef,0xf5,0xdd,0x7b,0xfb,0xfe,0xbf,0xbf,0xdf,0xff,0xf6,0x7f,0xff,0xff,0xfc,0xef,0xf7,0xfb,0xfd,0xff,0x03,0xfe,0xff,0xfd,0xff,0xfd,0xff,0xff,0xf7,0xfd,0xff,0xfe,0xff,0xff,0x7f,0xff,0xdf,0xff,0xff,0xfd,0xff,0xfd,0x07,0x80,0xf7,0xfd,0xef,0xfc,0x00,0x70,0x7f,0xbf,0xdf,0xff,0xff,0xfe,0xff,0xfd,0xff,0xfe,0x83,0xc0,0xef,0xfe,0xdf,0xff,0x7e,0x7f,0xee,0xff,0x3f,0x00,0x0f,0x00,0xef,0xee,0x6d,0xf7,0xef,0xfe,0xdf,0xff,0x0d,0xf8,0xee,0xfe,0xbf,0x7f,0xef,0x7d,0xdf,0xee,0x7f,0xff,0xf7,0xfd,0xdf,0xbf,0x7f,0xdf,0xf7,0xfd,0xbf,0x7f,0xef,0x7d,0xdf,0xee,0x07,0xe0,0xf7,0xfd,0xdf,0x7f,0x7f,0x3f,0x03,0xf8,0x3f,0x00,0xef,0x7d,0xdf,0xf6,0x7f,0xff,0xbb,0xfb,0xdd,0xff,0x7b,0x7f,0xff,0xfb,0xbf,0x7f,0xef,0x7d,0xff,0xfe,0x01,0x80,0xbd,0xc7,0x1d,0xf8,0x05,0xf0,0xfd,0xff,0xbf,0x7f,0x0f,0x00,0x03,0x00,0xff,0xfb,0xde,0xef,0xdd,0xff,0xbd,0xff,0x06,0xfc,0x3f,0x00,0xef,0x7d,0xff,0xfe,0x03,0xc0,0xdf,0xff,0xdd,0xff,0xbe,0x7f,0xf7,0xfd,0xbf,0x7f,0xef,0x7d,0xff,0xfe,0xdf,0xfb,0xef,0xff,0xdd,0x3f,0xde,0x1d,0xf7,0xfd,0xbf,0x7f,0xef,0x7d,0xff,0xfe,0xbf,0xfb,0xf7,0xfd,0xdd,0xff,0xde,0x7b,0xf7,0xfd,0xbf,0x7f,0xef,0x7d,0xff,0xfe,0xbf,0xfb,0xfb,0xfb,0xdd,0xff,0xee,0x7b,0xf7,0xfd,0x3f,0x00,0x0f,0x00,0xff,0xfe,0xff,0xfa,0x01,0xf0,0xdd,0xff,0x06,0x70,0x07,0xfc,0xbf,0x07,0xef,0x7f,0x6f,0xf6,0xbf,0xfd,0xfb,0x75,0x00,0xe0,0xe6,0x77,0xb6,0xfd,0x1f,0xb8,0x0f,0x00,0x6e,0xf7,0xbf,0xfd,0xfe,0xf5,0x7b,0xff,0xf7,0xff,0xdd,0xff,0xbf,0xbd,0xef,0xff,0x02,0xf4,0xbf,0xfd,0xfd,0xed,0x03,0x20,0x00,0x30,0x00,0xfc,0x7f,0xdb,0xef,0xff,0x6e,0x07,0xbb,0xdd,0x0d,0xc0,0xfd,0xff,0xf7,0xff,0xf7,0xff,0x0f,0x00,0x2c,0x80,0xf6,0xbb,0xb7,0xdd,0xef,0xfd,0xfd,0xff,0xf7,0x7f,0x00,0xfe,0xef,0xfd,0xee,0xfb,0x06,0xbc,0xb7,0xed,0xec,0xfd,0x0c,0xf0,0x03,0xfc,0xf7,0xff,0xff,0xfd,0xef,0xfb,0xf6,0xba,0xaf,0xed,0x0d,0xed,0xfc,0xff,0xfb,0x3d,0x00,0xfc,0x1f,0x00,0x6f,0xc0,0x82,0xba,0xaf,0xf5,0x6d,0x6d,0x0d,0xf0,0xf5,0xfe,0xb6,0xff,0xff,0xfe,0xef,0xfb,0xb6,0xda,0xaf,0xf5,0x6d,0xf5,0xfd,0xff,0xed,0xfe,0xd5,0xff,0xff,0xc0,0xef,0xeb,0xb6,0xd6,0xaf,0xf9,0x6d,0xf5,0xfd,0xff,0x5e,0x1f,0x00,0xf8,0x7f,0xde,0xef,0xdb,0xb6,0xee,0xbf,0xfd,0x65,0xfb,0x0d,0x70,0xbf,0xff,0xff,0xff,0xbf,0xed,0x2f,0x80,0x86,0xd6,0xbf,0xfd,0x49,0xdb,0xed,0xb7,0x5f,0x7f,0x00,0xff,0xcf,0xf3,0xef,0xff,0xf6,0xd6,0xbf,0xfd,0xad,0xd5,0xed,0xf7,0xe7,0x7e,0x7f,0xff,0xff,0x0c,0x0e,0x00,0xbe,0x3a,0x01,0x80,0xf7,0xce,0x0d,0xf0,0xf9,0x71,0x00,0xff,0x1f,0x7f,0xef,0xff,0x7e,0xbd,0xff,0xff,0x7b,0xdf,0xed,0x37,0xfe,0x7b,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
/*=========================================================
  变量
  =========================================================*/
byte x, y;
byte PX, PY; //玩家坐标
byte sx; //当前地图大小
byte KeyBack; //按键返回值
byte MAP[18][8]; //游戏地图
bool OMAP[18][8]; //遮挡地图
unsigned long GT; //游戏倒计时
/*
   0为空
   1-254 为距离
   255为地雷
*/
byte LEVEL = 1; //关卡
/*
     关卡 大小 雷数   难度
     1    8*8  10     16.625
     2    8*10 14     17.5
     3    8*12 18     18.75
     4    8*14 22     19.642
     5    8*16 26     20.312
     6    8*18 30     20.833

     大小:8*(8+(n-1)*2)
     雷数:10+(n-1)*4
*/
byte mines[2];
/*=========================================================
  只循环一次
  =========================================================*/
void setup() {
  u8g2.begin();
  //初始化按钮
  pinMode(UP, INPUT);//定义方向UP引脚状态
  pinMode(DOWN, INPUT);//定义方向DOWN引脚状态
  pinMode(LEFT, INPUT);//定义方向LEFT引脚状态
  pinMode(RIGHT, INPUT);//定义方向RIGHT引脚状态
  pinMode(A, INPUT);//定义按键A引脚状态
  pinMode(B, INPUT);//定义按键B引脚状态
  Serial.begin(9600);
  u8g2.setFont(u8g2_font_5x7_tf);
  
    for (int i = 63; i >= -8; i--) {
      u8g2.clearBuffer();
     // u8g2.drawXBMP(26, i, 72, 8, HWT);
      //u8g2.drawXBMP(0, i + 8, 128, 64, CSV);
      u8g2.sendBuffer();
    }
  
  delay(1000);
  GenerateMap(); //生成地图
}
/*=========================================================
                     不停循环
  =========================================================*/
void loop() {
  key(); //按键扫描
  logic(); //逻辑
  draw(); //绘图
  //DEBUG();
}

/*=========================================================
  生成地图 和 初始化游戏
  =========================================================*/
void GenerateMap() {
  sx = 8 + (LEVEL - 1) * 2;
  PX = sx / 2.0;
  PY = 4;
  //清空地图
  for (y = 0; y < 8; y++) {
    for (x = 0; x < sx; x++) {
      MAP[x][y] = 0;
      OMAP[x][y] = true;
    }
  }
  //生成地雷
  mines[1] = 10 + (LEVEL - 1) * 4; //计算当前关卡要生成颗地雷
  for (mines[0] = 0; mines[0] < mines[1];) {
    //随机生成地雷坐标
    x = random(sx);
    y = random(8);
    if (MAP[x][y] == 0) {  //判断随机位置是否为地雷，是的话重新生成
      //  Serial.println(String("[ ") + x + String(" ] ") + String("[ ") + y + String(" ]"));
      MAP[x][y] = 255; //设置地雷
      mines[0]++;
    }
  }
  mines[0] = 0;
  //计算与雷的距离
  for (y = 0; y < 8; y++) {
    for (x = 0; x < sx; x++) {
      if (MAP[x][y] != 255) {
        //该位置不是雷，计算附近3*3有多少雷
        for (int my = -1; my < 2; my++) {
          for (int mx = -1; mx < 2; mx++) if (MAP[x + mx][y + my] == 255 && x + mx >= 0 && y + my >= 0 && x + mx < sx && y + my < 8) MAP[x][y]++;
        }
      }
    }
  }
  GT = millis() / 1000;
}
/*=========================================================
  按键扫描
  =========================================================*/
void key() {
  /*
      0  1  2  3  4  5
      ↑ ↓← →  A  B
  */
  KeyBack = 255;
  if (digitalRead(UP) == LOW) KeyBack = 0;
  if (digitalRead(DOWN) == LOW) KeyBack = 1;
  if (digitalRead(LEFT) == LOW) KeyBack = 2;
  if (digitalRead(RIGHT) == LOW) KeyBack = 3;
  if (digitalRead(A) == LOW) KeyBack = 4;
  if (digitalRead(B) == LOW) KeyBack = 5;
}
/*=========================================================
  绘图
  =========================================================*/
void draw() {
  u8g2.clearBuffer();
    byte x, y;
    u8g2.setCursor(0, 8);
    //显示扫雷地图
    mines[0] = 0;
    for (y = 0; y < 8; y++) {
      for (x = 0; x < sx; x++) {
        u8g2.setCursor(x * 6, y * 8 + 8);
        if (OMAP[x][y] == false || MAP[PX][PY] == 254) {
          if (MAP[x][y] == 255) {
            u8g2.print(F("^"));
          } else if (MAP[x][y] == 0) {
            u8g2.print(F("."));
          } else {
            if (MAP[x][y] != 254) u8g2.print(MAP[x][y]);
          }
        } else {
          u8g2.print(F("+"));
          mines[0]++;
        }
      }
    }
    //判定是否通关
    if (mines[0] == mines[1]) win();
    //显示状态栏
    u8g2.drawLine(108, 0, 108, 64);
    u8g2.setCursor(110, 24 + 8);
    u8g2.print(mines[0]);
    u8g2.setCursor(116, 32 + 8);
    u8g2.print(F("/"));
    u8g2.setCursor(110, 40 + 8);
    u8g2.print(mines[1]);
    //显示光标 以及状态栏表情
    u8g2.setCursor(PX * 6, PY * 8 + 8);
    if (MAP[PX][PY] == 254) {
      u8g2.print(F("*"));
      u8g2.setCursor(110, 8);
      u8g2.print(F(":("));
    } else {
      u8g2.print(F("@"));
      u8g2.setCursor(110, 8);
      u8g2.print(F(":)"));
    }
    //显示游戏剩余时间
    u8g2.setCursor(110, 16);
    u8g2.print(300 + GT - millis() / 1000);
  u8g2.sendBuffer();
}
/*=========================================================
  DEBUG
  =========================================================*/
void DEBUG() {
  Serial.println("=======DEBUG=======");
  for (y = 0; y < 8; y++) {
    for (x = 0; x < sx; x++) {
      if (MAP[x][y] == 255) {
        Serial.print(F("X"));
      } else {
        Serial.print(MAP[x][y]);
      }
    }
    Serial.println();
  }
  Serial.println("=======OMAP=======");
  for (y = 0; y < 8; y++) {
    for (x = 0; x < sx; x++) {
      Serial.print(OMAP[x][y]);
    }
    Serial.println();
  }
  Serial.println();
  Serial.print(F(""));
  Serial.println();
}
/*=========================================================
  逻辑
  =========================================================*/
void logic() {
  //按键返回
  switch (KeyBack) {
    case 0:
      if (PY != 0) PY--;
      break;
    case 1:
      if (PY != 7) PY++;
      break;
    case 2:
      if (PX != 0) PX--;
      break;
    case 3:
      if (PX < sx - 1) PX++;
      break;
    case 4:
      OMAP[PX][PY] = false; //做标记 这个位置挖开了
      if (MAP[PX][PY] == 255) {
        MAP[PX][PY] = 254;
        fail();
      } else if (MAP[PX][PY] == 0) {
        BOOM();
      }
      break;
    case 5:
      break;
  }
  //判断是否超时
  if (millis() / 1000.0 >= GT + 300) fail(); //游戏超时 游戏结束
}
/*=========================================================
                  下一关
  =========================================================*/
void win() {
  u8g2.clearBuffer();
    for (y = 0; y < 8; y++) {
      for (x = 0; x < sx; x += 3) {
        u8g2.setCursor(x * 6, y * 8 + 8);         //设置光标
        u8g2.print(F("WIN"));                 //打印 你赢了
        u8g2.sendBuffer();
        delay(10);
      }
    }
  delay(5000);
  if (LEVEL != 6) LEVEL++;
  GenerateMap();
}
/*=========================================================
                  失败
  =========================================================*/
void fail() {
  draw();
  delay(5000);
  GenerateMap();
}
/*=========================================================
  BOOM  炸开一片区域 通常程序会在空白时候调用
  =========================================================*/
void BOOM() {
SBOOM:
  MAP[PX][PY] = 253;
  bool Change = false;
  for (y = 0; y < 8; y++) {
    for (x = 0; x < sx; x++) {
      //开始遍历地图
      for (int my = -1; my < 2; my++) {
        for (int mx = -1; mx < 2; mx++) {
          if (x + mx >= 0 && y + my >= 0 && x + mx < sx && y + my < 8) { //防止内存越界
            if (MAP[x + mx][y + my] == 0 && MAP[x][y] == 253) {
              Change = true;
              MAP[x + mx][y + my] = 253;
            }
          }
        }
      }
    }
  }
  if (Change == true) goto SBOOM;
  for (y = 0; y < 8; y++) {
    for (x = 0; x < sx; x++) {
      if (MAP[x][y] == 253) {
        MAP[x][y] = 0;
        for (int my = -1; my < 2; my++) {
          for (int mx = -1; mx < 2; mx++) {
            if (x + mx >= 0 && y + my >= 0 && x + mx < sx && y + my < 8) { //防止内存越界
              OMAP[x + mx][y + my] = false;
            }
          }
        }
      }
    }
  }
}

